package bancosys.tec.security.impl.services.incluir;

import java.lang.reflect.TypeVariable;

import jmine.tec.database.pack.PackException;
import jmine.tec.database.pack.services.PackEntityPersistenceHelper;
import jmine.tec.di.annotation.Injected;
import jmine.tec.security.impl.SecurityMessages;
import jmine.tec.services.annotations.Execution;
import jmine.tec.services.annotations.Input;
import jmine.tec.services.annotations.Output;
import jmine.tec.services.annotations.ServiceImplementor;
import jmine.tec.services.api.ActionsEnum;
import jmine.tec.services.api.ServiceExecutionException;
import jmine.tec.services.api.export.ReferenceMap;
import jmine.tec.services.api.export.ServiceFiller;
import jmine.tec.services.export.impl.ExportUtils;
import jmine.tec.services.io.ServiceBean;
import jmine.tec.utils.reflection.GenericTypeResolverImpl;
import bancosys.tec.security.impl.domain.Credential;
import bancosys.tec.security.impl.domain.MetaCredential;
import bancosys.tec.security.impl.domain.SecurityClient;

/**
 * Classe base para inclusão de credenciais no sistema
 * 
 * @author seiti
 * @created Oct 11, 2010
 * @param <C> entidade que estenda Credential
 */
@ServiceImplementor(action = ActionsEnum.INCLUIR)
public abstract class AbstractCredentialService<C extends Credential> implements ServiceFiller<C> {

    public static final String DATABASE_ID = "Database ID";

    public static final String IDENTIFICADOR = "Identificador";

    public static final String SECURITY_CLIENT = "Cliente da Segurança";

    public static final String MNEMONICO = "Mnemônico";

    public static final String META_CREDENTIAL = "Meta Credencial";

    // Injected
    private PackEntityPersistenceHelper helper;

    // Parameters
    private Long databaseId;

    private SecurityClient securityClient;

    private MetaCredential metaCredential;

    private String mnemonico;

    /**
     * As subclasses devem implementar este método, associando valores às propriedades da credencial dada.
     * 
     * @param credential credencial para preencher
     */
    protected abstract void fillCredential(C credential);

    /**
     * Realiza o serviço
     * 
     * @return uma credencial
     * @throws ServiceExecutionException ServiceExecutionException
     * @throws PackException PackException
     */
    @Execution
    @Output(propertyName = IDENTIFICADOR)
    public final C execute() throws ServiceExecutionException, PackException {
        C credential = this.helper.findOrCreate(this.getCredentialClass(), this.databaseId, this.mnemonico);
        credential.setClient(this.securityClient);
        credential.setMnemonico(this.mnemonico);
        if (this.metaCredential != null) {
            this.metaCredential.addCredential(credential);
        }
        this.fillCredential(credential);
        this.helper.save(credential, this.databaseId);
        return credential;
    }

    /**
     * {@inheritDoc}
     */
    public void fillServiceBean(ServiceBean bean, ReferenceMap referenceMap, C entity) {
        ExportUtils.setService(bean, this.getClass());
        ExportUtils.put(bean, IDENTIFICADOR, referenceMap.addReference(entity, "credential"));
        ExportUtils.put(bean, SECURITY_CLIENT, entity.getClient().getName());
        ExportUtils.put(bean, MNEMONICO, entity.getMnemonico());
        ExportUtils.putDatabaseId(bean, DATABASE_ID, entity);
        if (entity.getMetaCredential() != null) {
            ExportUtils.put(bean, META_CREDENTIAL, referenceMap, entity.getMetaCredential(), entity.getMetaCredential().getName());
        }
    }

    /**
     * Devolve a classe de credencial
     * 
     * @return credential
     * @throws ServiceExecutionException ServiceExecutionException
     */
    @SuppressWarnings("unchecked")
    protected Class<C> getCredentialClass() throws ServiceExecutionException {
        try {
            TypeVariable typeVariable = AbstractCredentialService.class.getTypeParameters()[0];
            return (Class<C>) GenericTypeResolverImpl.getInstance().resolveTypeForFixedGenericType(typeVariable, this.getClass());
        } catch (IllegalArgumentException e) {
            throw new ServiceExecutionException(SecurityMessages.INVALID_CREDENTIAL_SERVICE_IMPLEMENTATION.create(this.getClass()), e);
        }
    }

    /**
     * @param helper the helper to set
     */
    @Injected
    public void setHelper(PackEntityPersistenceHelper helper) {
        this.helper = helper;
    }

    /**
     * @param databaseId the databaseId to set
     */
    @Input(fieldName = DATABASE_ID, required = false)
    public void setDatabaseId(Long databaseId) {
        this.databaseId = databaseId;
    }

    /**
     * @param securityClient the securityClient to set
     */
    @Input(fieldName = SECURITY_CLIENT, cached = true)
    public void setSecurityClient(SecurityClient securityClient) {
        this.securityClient = securityClient;
    }

    /**
     * @param mnemonic the mnemonic to set
     */
    @Input(fieldName = MNEMONICO)
    public void setMnemonico(String mnemonic) {
        this.mnemonico = mnemonic;
    }

    /**
     * @param metaCredential the metaCredential to set
     */
    @Input(fieldName = META_CREDENTIAL, required = false)
    public void setMetaCredential(MetaCredential metaCredential) {
        this.metaCredential = metaCredential;
    }
}
